Skip to content

feat(host): add Qwen Code host adapter - #6

Merged
phodal merged 5 commits into
QoderAI:mainfrom
GCGH159:main
Jul 29, 2026
Merged

feat(host): add Qwen Code host adapter#6
phodal merged 5 commits into
QoderAI:mainfrom
GCGH159:main

Conversation

@GCGH159

@GCGH159 GCGH159 commented Jul 28, 2026

Copy link
Copy Markdown

Summary

Add Qwen Code as a fifth supported host, alongside Qoder, Codex, Claude Code, and Cursor.

What's new

  • Session analyzer (scripts/session-analysis/platforms/qwen.mjs): parses ~/.qwen/projects/<slug>/chats/*.jsonl transcripts using the message.parts functionCall/functionResponse format and top-level toolCallResult, mapping usageMetadata to standard usage fields.
  • Asset collector (scripts/agent-customize/providers/qwen.mjs): inventories ~/.qwen/{skills,agents,rules,commands,hooks,extensions} + workspace .qwen + QWEN.md + ~/.mcp.json.
  • Plugin shell (.qwen-plugin/plugin.json)
  • Best practices (references/agent-customize/platforms/qwen.md)

Changes

  • 4 new files + 41 modified (registration points, platform whitelists, help/USAGE text, docs, tests)
  • All 832 tests pass; pack:verify passes

Verification

Validated against real Qwen Code session data:

  • session-analysis sources --platform qwen discovers real sessions
  • session-analysis facts --platform qwen outputs session-core-facts (schemaVersion 3)
  • harness evidence-bundle --platform qwen — all 3 lanes (sessionEvidence + projectHarness + agentCustomize) available

@GCGH159

GCGH159 commented Jul 28, 2026

Copy link
Copy Markdown
Author

ok

Copilot AI left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

该 PR 将 Qwen Code 作为第五个受支持的 Host 平台接入 Better Harness(与 Qoder / Codex / Claude Code / Cursor 并列),补齐会话证据(session-analysis)、资产盘点(agent-customize)、插件壳(plugin shell)、打包校验与文档/测试覆盖。

Changes:

  • 新增 Qwen 会话分析适配器:读取 ~/.qwen/projects/<slug>/chats/**/*.jsonl,归一化 message.parts 的 functionCall/functionResponse 与 toolCallResult
  • 新增 Qwen 资产盘点适配器:盘点 ~/.qwen/{skills,agents,rules,commands,hooks,extensions}、工作区 .qwen/QWEN.md 以及 ~/.mcp.json
  • 增加 .qwen-plugin/plugin.json 并更新 npm 打包/校验、CLI help、参考文档与测试用例以纳入 qwen 平台。

Reviewed changes

Copilot reviewed 45 out of 45 changed files in this pull request and generated 1 comment.

Show a summary per file
File Description
test/session-analysis-providers.test.mjs 增加 Qwen provider 的 session 事件展开与 workspace slug 覆盖测试。
test/scripts-refactor-contract.test.mjs 更新 CLI 输出契约哈希以反映新增平台。
test/plugin-manifests.test.mjs .qwen-plugin/plugin.json 纳入宿主插件清单一致性与打包包含性断言。
test/fixtures/scripts-refactor-contract/session-help.txt session-analysis 帮助文本加入 qwen
test/fixtures/scripts-refactor-contract/root-help.txt 根命令 help 文本更新以包含 Qwen。
test/coding-agent-platform-notes.test.mjs sessions-diagnostics 平台说明加入 Qwen 并断言排序/内容。
test/better-harness-evidence-bundle.test.mjs evidence-bundle 的 agentCustomize lane 覆盖 Qwen 路由。
test/agent-customize.test.mjs 增加 Qwen fixture 与 inventory 断言(插件、MCP、skills/hooks/rules/commands/agents)。
test/agent-customize-architecture.test.mjs 架构/社区/术语文档断言从“四个平台壳”更新到“五个平台壳”。
test/agent-asset-integrity.test.mjs asset-integrity CLI 增加 Qwen 路径与隐私不泄漏断言。
test/agent-asset-baseline.test.mjs asset-baseline 增加 Qwen provider 的基线收集测试。
scripts/session-analysis/usage-summary.mjs --platform 参数说明加入 qwen。
scripts/session-analysis/selection-profile.mjs --platform 参数说明加入 qwen。
scripts/session-analysis/platforms/qwen.mjs 新增 Qwen 会话分析器实现。
scripts/session-analysis/lifecycle-demand-signals.mjs 平台白名单加入 qwen。
scripts/session-analysis/analyzer.mjs loader/帮助与错误信息加入 qwen + 动态 import。
scripts/session-analysis.mjs 顶层 shim loader/usage 加入 qwen + 动态 import。
scripts/npm-package/verify-pack.mjs pack 校验:版本对齐、必需文件、runtime bundle 禁止项加入 .qwen-plugin/
scripts/harness-analysis/task-loop-source.mjs task-loop source 支持 qwen(平台列表、asset review 支持、qwenHome 透传、inventory 路由)。
scripts/harness-analysis/task-loop-report.mjs 报告平台白名单加入 qwen。
scripts/harness-analysis/report-run.mjs report-run 平台白名单与 CLI 允许参数加入 qwen-home
scripts/harness-analysis/report-quality.mjs 会话范围识别正则加入 .qwen / qwen。
scripts/harness-analysis/evidence-bundle/contract.mjs evidence-bundle provider 集合加入 qwen。
scripts/harness-analysis/evidence-bundle/cli.mjs evidence-bundle CLI 平台说明与允许参数加入 qwen。
scripts/harness-analysis/evidence-bundle/agent-customize.mjs agentCustomize lane provider 白名单加入 qwen。
scripts/coding-agent-practices/inventory.mjs inventory CLI/平台白名单加入 qwen。
scripts/coding-agent-practices/asset-integrity.mjs asset-integrity CLI provider 白名单与 usage 加入 qwen。
scripts/coding-agent-practices/asset-baseline.mjs asset-baseline provider 白名单、usage、错误信息加入 qwen。
scripts/better-harness-cli/registry.mjs CLI registry 描述更新以包含 Qwen。
scripts/agent-lint/cli.mjs agent-lint provider usage 加入 qwen。
scripts/agent-customize/providers/qwen.mjs 新增 Qwen agent-customize provider 实现。
scripts/agent-customize/providers/index.mjs provider collectors map 注册 qwen。
scripts/agent-customize/cli.mjs summary 透出 qwenHome
references/session-evidence/sessions-diagnostics.md sessions diagnostics 文档加入 Qwen 平台说明与命令示例。
references/loop-engineering/loop-discovery.md loop-discovery 文案更新以包含 Qwen 会话证据。
references/agent-customize/routing.md agent-customize routing 增加 Qwen Asset Route。
references/agent-customize/platforms/qwen.md 新增 Qwen Code best practices 参考文档。
package.json npm files 列表加入 .qwen-plugin/
docs/glossary.md glossary 补充 Qwen 平台与 host shell 列表/描述更新。
docs/concepts.md concepts 中 session evidence 描述加入 Qwen。
docs/community.md community 中 host shell/packaging 条目加入 Qwen。
docs/ARCHITECTURE.md 架构说明从 4 个插件壳更新为 5 个。
docs/adrs/directory-structure.md 目录结构 ADR 更新 host adapter 列表与插件壳数量。
docs/adapters/README.md Host Adapter Matrix 新增 Qwen Code 行与说明;输出模式描述更新。
.qwen-plugin/plugin.json 新增 Qwen 插件壳清单。

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment thread scripts/session-analysis/platforms/qwen.mjs
GCGH159 added a commit to GCGH159/better-harness that referenced this pull request Jul 28, 2026
When toolCallResult.resultDisplay is absent (8.9% of real tool_result rows),
fall back to functionResponse.response.output for resultFacts extraction.
Also use functionResponse.id as a fallback for callId correlation.

Addresses Copilot review comment on PR QoderAI#6.
@GCGH159

GCGH159 commented Jul 28, 2026

Copy link
Copy Markdown
Author

@copilot thanks for the catch — this is a real gap, not hypothetical.

Validated against real Qwen Code session data (24,207 tool_result rows across all local transcripts): 8.9% (2,150 rows) lack toolCallResult.resultDisplay, but every one of those rows carries functionResponse.response.output with the actual tool output text. So without a fallback, resultFacts was empty for ~9% of tool results.

Fixed in 593d1b2: tool_result now falls back to functionResponse.id for callId correlation and functionResponse.response.output for resultFacts extraction when resultDisplay is absent. All 24,207 rows now have a correlatable id; the 2,150 previously-empty rows now get resultFacts from the response.output payload.

All 10 session-analysis-providers tests still pass.

@phodal

phodal commented Jul 28, 2026

Copy link
Copy Markdown
Member

Review result: request changes

I reviewed this PR in an isolated worktree and cross-checked the adapter against the local Qwen Code 0.21.0 source at QwenLM/qwen-code@63166bd5445c5f3110c54a73c4968a454ca2cfb8. I also ran a logged-in Qwen review over the focused diff. The direction is useful, but the current implementation encodes several contracts that do not match the Qwen runtime, so I do not think this is merge-ready.

Blocking findings

  1. [P1] The published Qwen extension manifest is not a native Qwen manifest.

    This PR adds .qwen-plugin/plugin.json and packages/tests that path. Qwen Code defines the native manifest as a root-level qwen-extension.json (EXTENSIONS_CONFIG_FILENAME) and its loader fails if that file is absent:

    Local reproduction with qwen extensions link fails with Configuration file not found .../qwen-extension.json. The package metadata, verify-pack, docs, and plugin-manifests tests currently reinforce the wrong contract rather than testing native installation.

  2. [P1] Session discovery conflates QWEN_HOME with the runtime/session directory.

    resolveScope creates one home, then discoverSourceRoots always reads home/projects/<slug>/chats.

    Qwen keeps these concepts separate: QWEN_HOME controls global configuration, while QWEN_RUNTIME_DIR controls session data, todos, logs, and project directories. See storage.ts#L138-L173 and storage.ts#L326-L329. When the two env vars point to different directories, this adapter misses all sessions.

  3. [P1] workspaceToQwenSlugVariants does not implement Qwen's project-id algorithm.

    The PR only replaces a small set of punctuation and separators: qwen.mjs#L24-L30. Native sanitizeCwd replaces every non-ASCII-alphanumeric character with -, and lowercases first on Windows: paths.ts#L375-L379.

    Paths containing spaces, Chinese characters, parentheses, or Windows case differences resolve to a different project directory. The current tests use only simple synthetic paths and therefore codify the mismatch.

  4. [P1] Extension inventory reads the wrong manifest and computes enablement incorrectly.

    The provider reads .qwen-plugin/plugin.json at providers/qwen.mjs#L125-L164, then checks a nonexistent record.enablement.disabled boolean.

    Native Qwen reads qwen-extension.json and calculates activation from ordered overrides path rules for the current lexical/canonical workspace path: extensionManager.ts#L560-L604. A disabled extension such as one projected with !/* is currently reported as enabled, and its skills/assets are included.

  5. [P1] User and project MCP/hooks configured in settings.json are omitted.

    collectQwenUserPrimitives and collectQwenWorkspacePrimitives only scan .mcp.json/mcp.json, hooks.json, and hook directories.

    Qwen also loads mcpServers and hooks directly from user/workspace settings.json; see config.ts#L2022-L2029, config.ts#L2293-L2299, and settings.ts#L577-L593. These are normal native configurations, not edge cases.

  6. [P2] A cancelled tool result is classified as successful.

    qwen.mjs#L174-L180 excludes only error and failed. Qwen emits cancelled as a distinct non-success terminal state, so cancelled operations currently inflate tool success metrics.

  7. [P2] qwenHome is lost in the public inventory path.

    The Qwen provider accepts and returns qwenHome, but providerScope omits it and collectProviderInventory never forwards it. Markdown formatting also has no Qwen home branch. An explicit --qwen-home therefore disappears from the public report path.

Review readiness

  • Story evidence: no separate Story/issue evidence is visible in the PR or commits.
  • Spec evidence: missing. This is a non-trivial new host adapter, but there is no matching docs/specs/... document with acceptance scenarios, non-goals, test evidence, and risks as required by the repository instructions.
  • Tests: local npm test passed 832/832 on a clean rerun and npm run pack:verify passed. However, several Qwen fixtures mirror the adapter's invented .qwen-plugin/disabled contracts, so these results demonstrate internal consistency, not native Qwen compatibility.
  • AI evidence: no explicit AI: or Co-authored-by marker is visible; no inference made.
  • CI: GitHub currently reports no status checks for this PR.
  • Merge state: currently CONFLICTING / DIRTY; test/agent-customize.test.mjs conflicts with current main.
  • Scope: 45 files, +1342/-69. Most breadth is registration/docs/tests, but the two core adapters need to be corrected before that propagation is trustworthy.

Suggested repair sequence

  1. Add a native root qwen-extension.json; update packaging, docs, and manifest tests, and prove it with an isolated qwen extensions link smoke test.
  2. Separate config home from runtime/session home and honor QWEN_HOME plus QWEN_RUNTIME_DIR.
  3. Match native sanitizeCwd exactly and add spaces, Unicode, punctuation, and Windows-case fixtures.
  4. Read user/workspace settings.json for MCP/hooks and test precedence/trust behavior.
  5. Evaluate extension activation from overrides for the target workspace.
  6. Treat cancelled as non-success and thread qwenHome through public inventory/Markdown output.
  7. Add the required spec, rebase onto current main, resolve the test conflict, and rerun the full suite plus native Qwen smoke tests.

The logged-in Qwen review independently flagged the same seven implementation gaps. One wording correction from that model output: lowercasing is Windows-only; replacing all non-alphanumeric characters applies on every platform.

GCGH159 added a commit to GCGH159/better-harness that referenced this pull request Jul 29, 2026
When toolCallResult.resultDisplay is absent (8.9% of real tool_result rows),
fall back to functionResponse.response.output for resultFacts extraction.
Also use functionResponse.id as a fallback for callId correlation.

Addresses Copilot review comment on PR QoderAI#6.
@GCGH159

GCGH159 commented Jul 29, 2026

Copy link
Copy Markdown
Author

@phodal thanks for the thorough review — all 7 findings confirmed against Qwen Code 0.21.0 source and fixed in 59541aa (rebased onto current main, conflict resolved).

What changed

  1. [P1] Native manifest.qwen-plugin/plugin.json removed; replaced with root-level qwen-extension.json matching EXTENSIONS_CONFIG_FILENAME. Packaging, verify-pack, manifest tests, and docs all updated.

  2. [P1] QWEN_HOME vs QWEN_RUNTIME_DIRresolveScope now resolves both: home (config, from QWEN_HOME / ~/.qwen) and runtimeDir (session data, from QWEN_RUNTIME_DIR env var, falling back to home). discoverSourceRoots reads runtimeDir/projects/<slug>/chats.

  3. [P1] Slug algorithmworkspaceToQwenSlugVariants now matches sanitizeCwd exactly: normalized.replace(/[^a-zA-Z0-9]/g, "-"), plus a lowercase variant for Windows compatibility.

  4. [P1] Extension enablement — reads qwen-extension.json for metadata; computes enabled from extension-enablement.json overrides using the same path-matching logic as Override.fromFileRule + matchesPath (glob → regex, leading/trailing slash normalization). Verified against real data: loop-engineering with ["!/home/admin/*", "/home/admin/loop-engineering-demo/*"] correctly shows enabled: false for workspace /home/admin/odps_irds.

  5. [P1] settings.json MCP/hooks — user primitives now also read mcpServers and hooks from ~/.qwen/settings.json; workspace primitives from <workspace>/.qwen/settings.json. Deduped against .mcp.json / hooks.json entries.

  6. [P2] cancelled statustcr.status === "cancelled" now treated as non-success (verified: real transcripts contain all three statuses: success, error, cancelled).

  7. [P2] qwenHome threading — added to providerScope(), collectProviderInventory() forwarding, and userBase fallback chain in inventory.mjs.

Verification

  • 840/840 tests pass (including 8 new upstream tests from rebase)
  • pack:verify passes (npm 307, runtime zip 333)
  • Real-data smoke test: sources --platform qwen and inventory --provider qwen both work against live ~/.qwen data

Still open

  • Spec doc (docs/specs/...): will add if required for merge.
  • Native qwen extensions link smoke test: the manifest is now in the correct format; happy to add a CI step if desired.

@GCGH159

GCGH159 commented Jul 29, 2026

Copy link
Copy Markdown
Author

CI fix in b464d06: macOS and Windows both failed test 584 ("Qwen provider expands function calls and tool results from parts") with responseCount 2 instead of 1.

Root cause: on case-insensitive filesystems (HFS+, NTFS), the two slug variants from workspaceToQwenSlugVariants (original case + lowercase) resolve to the same projects/ directory, so the session file was discovered and read twice.

Fix: dedupe resolved root paths via realpathSync.native before walking in discoverSessions.

@GCGH159

GCGH159 commented Jul 29, 2026

Copy link
Copy Markdown
Author

@phodal could you approve the pending workflow run for the latest push (b464d06)? It is currently in action_required state: https://github.com/QoderAI/better-harness/actions/runs/30424788142

This push fixes the macOS/Windows CI failure (case-insensitive filesystem slug dedup) on top of the review fixes.

@phodal

phodal commented Jul 29, 2026

Copy link
Copy Markdown
Member

Follow-up review of b464d06 — remaining changes requested

Thanks for the update. I pulled the new head after b464d06 and re-ran the focused checks.

Confirmed fixed:

  • the root qwen-extension.json now passes a real isolated qwen extensions link smoke test against Qwen Code 0.21.0;
  • QWEN_RUNTIME_DIR, cancelled tool results, and explicit --qwen-home threading are fixed;
  • b464d06 fixes the case-insensitive duplicate session read on this macOS checkout: the focused Qwen/provider/manifest tests now pass 34/34;
  • the current CI run is green on Ubuntu Node 22/24, macOS, and Windows.

However, “all 7 findings fixed” is not yet accurate. The following native-contract issues remain.

1. [P1] QWEN_HOME is still ignored unless passed as an explicit CLI option

The session analyzer defaults directly to ~/.qwen (qwen.mjs#L268-L277), and the customize provider does the same (providers/qwen.mjs#L329-L338). Neither fallback reads process.env.QWEN_HOME.

Reproduction on the latest head:

QWEN_HOME=/tmp/bh-pr6-qwen-env QWEN_RUNTIME_DIR=/tmp/bh-pr6-runtime-env ... sources
=> home=/Users/phodal/.qwen, runtimeDir=/tmp/bh-pr6-runtime-env

QWEN_HOME=/tmp/bh-pr6-qwen-env ... agent-customize inventory --provider qwen
=> qwenHome=/Users/phodal/.qwen

Native Qwen prioritizes QWEN_HOME in Storage.getGlobalQwenDir. This is especially blocking when QWEN_RUNTIME_DIR is unset, because native runtime data then falls back to QWEN_HOME while this analyzer searches the wrong tree. Please add the env fallback to both adapters and cover env-only configuration in tests.

2. [P1] settings.json hooks are still never parsed in Qwen's native shape

Both user and project collectors require Array.isArray(settings.hooks) and then expect direct { command } entries (providers/qwen.mjs#L259-L270, #L300-L310). Qwen defines hooks as an object keyed by event; each event contains hook definitions, and the executable command/HTTP hook is nested in each definition's hooks array (schema, hook item shape).

A normal { "hooks": { "PreToolUse": [{ "matcher": "...", "hooks": [{ "type": "command", "command": "..." }] }] } } configuration is therefore skipped entirely. Please flatten event → definition → hook entries and add native command plus HTTP-hook fixtures.

3. [P1] project .mcp.json is scanned at the wrong path, and settings-based remote MCPs lose their endpoint

The workspace collector scans collectWorkspaceRootPrimitives(path.join(workspace, ".qwen"), ...) (providers/qwen.mjs#L282-L296), so it looks for <workspace>/.qwen/.mcp.json. Native Qwen explicitly loads project MCP config from <projectRoot>/.mcp.json (mcpJson.ts#L15-L45). Conversely, the user collector's path.dirname(qwenHome)/.mcp.json is not the native user configuration path in 0.21.0; user MCP servers are in <QWEN_HOME>/settings.json.

Also, settings-based MCP items are manually reduced to only command and args (providers/qwen.mjs#L245-L255). Qwen accepts url and httpUrl transports as well, so an HTTP-only server is inventoried with neither a command nor an endpoint. Please read the root project file and normalize settings entries through a shared path that preserves safe transport metadata (url/httpUrl, env key names, disabled state) without exposing secret values.

4. [P2] extension enablement still omits canonical-path matching

isExtensionEnabled evaluates only the lexical workspace path. Native Qwen evaluates every override against both the lexical path and realpathSync.native(path.resolve(checkPath)) (extensionManager.ts#L581-L604). A workspace opened through a symlink can therefore be reported with the wrong extension state. The same canonicalization used by b464d06 should be applied here, with a symlink fixture.

Review readiness / smaller follow-ups

  • The required spec is still missing. This is a non-trivial new host adapter, and the repository instructions require a docs/specs/... document with stable acceptance IDs, non-goals, plan, test evidence, and risks.
  • agent-customize --help still omits Qwen and --qwen-home; Markdown inventory output still has no Qwen home line.
  • The Qwen reference still documents ~/.mcp.json as user MCP config and describes the old partial slug replacement rather than native sanitizeCwd.
  • The PR is currently three commits behind origin/main. A local merge-tree check is clean, but it should be rebased and the full matrix rerun after the remaining fixes.

So the native manifest/installation and CI regression are now closed, but I would still hold merge for items 1–3; item 4 and the spec should also be resolved before calling the adapter compatible with Qwen 0.21.0.

GCGH159 added 5 commits July 29, 2026 13:34
Add Qwen Code as a fifth supported host alongside Qoder, Codex, Claude Code,
and Cursor.

Session analyzer (scripts/session-analysis/platforms/qwen.mjs) parses
~/.qwen/projects/<slug>/chats/*.jsonl transcripts using the message.parts
functionCall/functionResponse format and top-level toolCallResult, mapping
usageMetadata to standard usage fields.

Asset collector (scripts/agent-customize/providers/qwen.mjs) inventories
~/.qwen/{skills,agents,rules,commands,hooks,extensions} plus workspace .qwen,
QWEN.md, AGENTS.md, and ~/.mcp.json.

New files: .qwen-plugin/plugin.json, session-analysis/platforms/qwen.mjs,
agent-customize/providers/qwen.mjs, references/agent-customize/platforms/qwen.md.
Updated: registration points, platform whitelists, help/USAGE text, docs
(sessions-diagnostics, adapters/README matrix, routing, ARCHITECTURE,
community, glossary), and 7 test files.

832 tests pass; pack:verify passes.
When toolCallResult.resultDisplay is absent (8.9% of real tool_result rows),
fall back to functionResponse.response.output for resultFacts extraction.
Also use functionResponse.id as a fallback for callId correlation.

Addresses Copilot review comment on PR QoderAI#6.
…ug, enablement, settings.json

Address all 7 findings from phodal's review:

P1: Replace .qwen-plugin/plugin.json with native qwen-extension.json at
repo root, matching Qwen's EXTENSIONS_CONFIG_FILENAME contract.

P1: Separate QWEN_HOME (config) from QWEN_RUNTIME_DIR (session data)
in resolveScope; discoverSourceRoots now reads from runtimeDir.

P1: Match native sanitizeCwd exactly — replace every non-alphanumeric
character with '-', lowercase variant for Windows compatibility.

P1: Read qwen-extension.json for extension metadata; compute enablement
from extension-enablement.json overrides path rules instead of a
nonexistent disabled boolean.

P1: Read mcpServers and hooks from user/workspace settings.json in
addition to .mcp.json and hooks.json.

P2: Treat cancelled tool_result status as non-success.

P2: Thread qwenHome through providerScope, collectProviderInventory,
and userBase fallback in inventory.mjs.

Also: rebase onto current main, resolve test/agent-customize.test.mjs
conflict. 840 tests pass; pack:verify passes.
…tems

On macOS (case-insensitive HFS+), two slug variants differing only in
case resolve to the same projects directory, causing session files to be
read twice and inflating responseCount. Use realpathSync.native to
dedupe resolved root paths before walking.
…P paths, canonical enablement

1. [P1] Read process.env.QWEN_HOME as fallback in both session analyzer
   resolveScope and customize provider defaultQwenHome.

2. [P1] Parse settings.json hooks in native Qwen shape: object keyed by
   event name → array of definitions → nested hooks array. Supports both
   command and http hook types.

3. [P1] User MCP now reads from <QWEN_HOME>/settings.json mcpServers
   (not ~/.mcp.json). Project MCP reads from <workspace>/.mcp.json (not
   <workspace>/.qwen/.mcp.json). Preserve url/httpUrl transport metadata.

4. [P2] Extension enablement now evaluates overrides against both lexical
   and canonical (realpathSync.native) workspace paths, matching native
   Qwen behavior for symlinked workspaces.

Also: agent-customize --help adds qwen/--qwen-home; reference doc
updated for native MCP paths and sanitizeCwd slug algorithm; rebased
onto current main. 866 tests pass; pack:verify passes.
@GCGH159

GCGH159 commented Jul 29, 2026

Copy link
Copy Markdown
Author

@phodal follow-up fixes pushed in 5af2afb (rebased onto 305ee58). All 4 items addressed and locally verified:

1. [P1] QWEN_HOME env fallback

Both adapters now read process.env.QWEN_HOME in the fallback chain:

  • Session analyzer: options.home ?? options.qwenHome ?? options["qwen-home"] ?? process.env.QWEN_HOME ?? "~/.qwen"
  • Provider: defaultQwenHome() returns process.env.QWEN_HOME ?? ~/.qwen

Verified: QWEN_HOME=/tmp/test-qwen-env → both resolveScope and collectQwenCustomizeInventory resolve to the env path. When QWEN_RUNTIME_DIR is unset, runtimeDir correctly falls back to the env-based home.

2. [P1] Native hooks shape

Replaced Array.isArray(settings.hooks) with flattenSettingsHooks() that parses the native object-keyed-by-event format: { "PreToolUse": [{ matcher, hooks: [{ type, command }] }] }. Supports both command and http (url) hook types.

Verified: fixture with PreToolUse command hook + Stop http hook → all three entries collected.

3. [P1] MCP paths + transport metadata

  • User MCP: now reads from <QWEN_HOME>/settings.json mcpServers (removed path.dirname(qwenHome)/.mcp.json)
  • Project MCP: now reads from <workspace>/.mcp.json (the native project root path), plus <workspace>/.qwen/settings.json mcpServers
  • normalizeSettingsMcp() preserves url/httpUrl transport fields

Verified: settings-based { url: "https://mcp.example.com/sse" } → inventoried with url field; project .mcp.json discovered at workspace root.

4. [P2] Canonical path enablement

isExtensionEnabled now evaluates overrides against both the lexical path and realpathSync.native(path.resolve(workspace)), matching extensionManager.ts#L581-L604.

Verified: symlink /tmp/link-project → /tmp/real-project with override !/tmp/real-project/* → correctly reports enabled: false when accessed via the symlink.

Smaller items

  • agent-customize --help now lists qwen in providers and --qwen-home in home overrides
  • Reference doc updated: MCP paths corrected, slug description matches sanitizeCwd
  • Rebased onto current main (305ee58)

866/866 tests pass; pack:verify passes (npm 308, runtime 334).

The spec doc (docs/specs/...) is still outstanding — will add if required for merge.

@GCGH159

GCGH159 commented Jul 29, 2026

Copy link
Copy Markdown
Author

@phodal we have started using the adapter on a real internal project (a Python/Django service with ~6 months of Qwen Code session history). Sharing sanitized findings:

Smoke test: qwen extensions link

$ qwen extensions link /path/to/better-harness
Installing extension "Better Harness".
Do you want to continue? [Y/n]: Extension "better-harness" linked successfully and enabled.

The native qwen-extension.json manifest is correctly recognized by Qwen Code 0.21.0. The better-harness skill installs and is invocable via slash command.

Session analysis (sources + facts)

  • 592 eligible sessions discovered under ~/.qwen/projects/<slug>/chats/, slug correctly matches sanitizeCwd output
  • Stratified sampling selected 12 sessions across 4 evidence classes: boundary-change, lifecycle-demand, operation-control, read-only-work
  • 19 task episodes / 18 distinct requests extracted
  • Population coverage: 14/18 with result signals and assistant handoffs; 6/18 with execution friction; 0 with structured completion or reviewed checks
  • Diagnostic flags correctly surfaced no-change-evidence and no-reviewed-relevant-check-evidence — accurate for this project, where most sessions are operational/debugging rather than code-authoring

Asset inventory (agent-customize inventory)

  • 1 linked extension correctly discovered via .qwen-extension-install.json; enablement computed from overrides — shows enabled: false for this workspace (the override disables it outside a specific demo path), matching native Qwen behavior
  • 7 project-scoped skills discovered from .qwen/skills/
  • QWEN.md picked up as a project rule source
  • User MCP/hooks from settings.json parsed in native format

Evaluation

The adapter is functional for daily use on real Qwen Code session data. The session analysis correctly handles the message.parts format, toolCallResult correlation (including the functionResponse fallback for the ~9% of rows lacking resultDisplay), and usageMetadata mapping. The facts envelope produces meaningful lifecycle and friction signals.

The main gap is that this project has heavy operational/debugging usage and light code-authoring, so withChanges and withChecks coverage is low — but that is an accurate reflection of the actual workflow, not an adapter bug.

@phodal
phodal merged commit 1ad9642 into QoderAI:main Jul 29, 2026
4 checks passed
creayma-del pushed a commit to creayma-del/better-harness that referenced this pull request Jul 29, 2026
Resolve the docs/adapters/README.md matrix conflict introduced by the Qwen
Code host adapter (QoderAI#6): keep this branch's reviewed Cursor row (Default
Output pending U-03, runtime smoke pending U-04) and keep the new Qwen Code
row, updating its Smoke cell from the nonexistent
`harness prepare --platform qwen` -> `html-report` chain to the current
`harness analyze --platform qwen` -> `harness render --mode html --validate`
chain, matching A-05 AC-1 (no stale `harness prepare` / standalone
`html-report` strings in the matrix; `--platform qwen` is accepted by
scripts/harness-analysis/report-run.mjs).

Test: node --test better-harness-skill agent-customize-architecture doc-link-graph legacy-product-names (21/21)
AI: Assisted
creayma-del pushed a commit to creayma-del/better-harness that referenced this pull request Jul 30, 2026
Document an evidence-first workflow for adding Coding Agent hosts, drawing bounded lessons from PR QoderAI#6 and PR QoderAI#22. Link the canonical guide from contributor entry points and publish concise English and zh-Hans site routes.

The change follows docs/specs/2026-07-30-new-coding-agent-contribution-guide.md and was validated with the 7/7 documentation checks, 867/867 full tests, the two-locale Docusaurus build, package verification, and git diff checks.

Co-authored-by: Codex (GPT 5.6 Sol) <codex@openai.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants